/*
 * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.activation;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Properties;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INITIALIZE;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.SystemException;

import com.sun.corba.se.spi.activation.BadServerDefinition;
import com.sun.corba.se.spi.activation.RepositoryPackage.ServerDef;
import com.sun.corba.se.spi.activation._RepositoryImplBase;
import com.sun.corba.se.spi.activation.ServerAlreadyRegistered;
import com.sun.corba.se.spi.activation.ServerAlreadyInstalled;
import com.sun.corba.se.spi.activation.ServerAlreadyUninstalled;
import com.sun.corba.se.spi.activation.ServerNotRegistered;
import com.sun.corba.se.spi.legacy.connection.LegacyServerSocketEndPointInfo;
import com.sun.corba.se.spi.transport.SocketOrChannelAcceptor;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.impl.orbutil.ORBConstants;

import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.impl.logging.ActivationSystemException;

/**
 *
 * @author      Rohit Garg
 * @since       JDK1.2
 */
public class RepositoryImpl extends _RepositoryImplBase implements Serializable {

    // added serialver computed by the tool
    private static final long serialVersionUID = 8458417785209341858L;

    RepositoryImpl(ORB orb, File dbDir, boolean debug) {
        this.debug = debug;
        this.orb = orb;
        wrapper = ActivationSystemException.get(orb, CORBALogDomains.ORBD_REPOSITORY);

        // if databse does not exist, create it otherwise read it in
        File dbFile = new File(dbDir, "servers.db");
        if (!dbFile.exists()) {
            db = new RepositoryDB(dbFile);
            db.flush();
        } else {
            try {
                FileInputStream fis = new FileInputStream(dbFile);
                ObjectInputStream ois = new ObjectInputStream(fis);
                db = (RepositoryDB) ois.readObject();
                ois.close();
            } catch (Exception e) {
                throw wrapper.cannotReadRepositoryDb(e);
            }
        }

        // export the repository
        orb.connect(this);
    }

    private String printServerDef(ServerDef sd) {
        return "ServerDef[applicationName=" + sd.applicationName + " serverName=" + sd.serverName + " serverClassPath=" + sd.serverClassPath + " serverArgs=" + sd.serverArgs + " serverVmArgs=" + sd.serverVmArgs + "]";
    }

    public int registerServer(ServerDef serverDef, int theServerId) throws ServerAlreadyRegistered {
        int serverId;
        DBServerDef server = null;

        synchronized (db) {

            // check if server already registered
            Enumeration enumeration = db.serverTable.elements();
            while (enumeration.hasMoreElements()) {
                server = (DBServerDef) enumeration.nextElement();
                if (serverDef.applicationName.equals(server.applicationName)) {
                    if (debug)
                        System.out.println("RepositoryImpl: registerServer called " + "to register ServerDef " + printServerDef(serverDef) + " with " + ((theServerId == illegalServerId) ? "a new server Id" : ("server Id " + theServerId)) + " FAILED because it is already registered.");

                    throw (new ServerAlreadyRegistered(server.id));
                }
            }

            // generate a new server id
            if (theServerId == illegalServerId)
                serverId = db.incrementServerIdCounter();
            else
                serverId = theServerId;

            // add server def to the database
            server = new DBServerDef(serverDef, serverId);
            db.serverTable.put(new Integer(serverId), server);
            db.flush();

            if (debug)
                if (theServerId == illegalServerId)
                    System.out.println("RepositoryImpl: registerServer called " + "to register ServerDef " + printServerDef(serverDef) + " with new serverId " + serverId);
                else
                    System.out.println("RepositoryImpl: registerServer called " + "to register ServerDef " + printServerDef(serverDef) + " with assigned serverId " + serverId);

            return serverId;
        }
    }

    public int registerServer(ServerDef serverDef) throws ServerAlreadyRegistered, BadServerDefinition {
        // verify that the entry is valid
        LegacyServerSocketEndPointInfo endpoint = orb.getLegacyServerSocketManager().legacyGetEndpoint(LegacyServerSocketEndPointInfo.BOOT_NAMING);
        int initSvcPort = ((SocketOrChannelAcceptor) endpoint).getServerSocket().getLocalPort();
        ServerTableEntry entry = new ServerTableEntry(wrapper, illegalServerId, serverDef, (int) initSvcPort, "", true, debug);

        switch (entry.verify()) {
            case ServerMain.OK:
                break;
            case ServerMain.MAIN_CLASS_NOT_FOUND:
                throw new BadServerDefinition("main class not found.");
            case ServerMain.NO_MAIN_METHOD:
                throw new BadServerDefinition("no main method found.");
            case ServerMain.APPLICATION_ERROR:
                throw new BadServerDefinition("server application error.");
            default:
                throw new BadServerDefinition("unknown Exception.");
        }

        return registerServer(serverDef, illegalServerId);
    }

    public void unregisterServer(int serverId) throws ServerNotRegistered {

        DBServerDef server = null;
        Integer id = new Integer(serverId);

        synchronized (db) {

            // check to see if the server is registered
            server = (DBServerDef) db.serverTable.get(id);
            if (server == null) {
                if (debug)
                    System.out.println("RepositoryImpl: unregisterServer for serverId " + serverId + " called: server not registered");

                throw (new ServerNotRegistered());
            }

            // remove server from the database
            db.serverTable.remove(id);
            db.flush();
        }

        if (debug)
            System.out.println("RepositoryImpl: unregisterServer for serverId " + serverId + " called");
    }

    private DBServerDef getDBServerDef(int serverId) throws ServerNotRegistered {
        Integer id = new Integer(serverId);
        DBServerDef server = (DBServerDef) db.serverTable.get(id);

        if (server == null)
            throw new ServerNotRegistered(serverId);

        return server;
    }

    public ServerDef getServer(int serverId) throws ServerNotRegistered {
        DBServerDef server = getDBServerDef(serverId);

        ServerDef serverDef = new ServerDef(server.applicationName, server.name, server.classPath, server.args, server.vmArgs);

        if (debug)
            System.out.println("RepositoryImpl: getServer for serverId " + serverId + " returns " + printServerDef(serverDef));

        return serverDef;
    }

    public boolean isInstalled(int serverId) throws ServerNotRegistered {
        DBServerDef server = getDBServerDef(serverId);
        return server.isInstalled;
    }

    public void install(int serverId) throws ServerNotRegistered, ServerAlreadyInstalled {
        DBServerDef server = getDBServerDef(serverId);

        if (server.isInstalled)
            throw new ServerAlreadyInstalled(serverId);
        else {
            server.isInstalled = true;
            db.flush();
        }
    }

    public void uninstall(int serverId) throws ServerNotRegistered, ServerAlreadyUninstalled {
        DBServerDef server = getDBServerDef(serverId);

        if (!server.isInstalled)
            throw new ServerAlreadyUninstalled(serverId);
        else {
            server.isInstalled = false;
            db.flush();
        }
    }

    public int[] listRegisteredServers() {
        synchronized (db) {
            int i = 0;

            int servers[] = new int[db.serverTable.size()];

            Enumeration enumeration = db.serverTable.elements();

            while (enumeration.hasMoreElements()) {
                DBServerDef server = (DBServerDef) enumeration.nextElement();
                servers[i++] = server.id;
            }

            if (debug) {
                StringBuffer sb = new StringBuffer();
                for (int ctr = 0; ctr < servers.length; ctr++) {
                    sb.append(' ');
                    sb.append(servers[ctr]);
                }

                System.out.println("RepositoryImpl: listRegisteredServers returns" + sb.toString());
            }

            return servers;
        }
    }

    public int getServerID(String applicationName) throws ServerNotRegistered {
        synchronized (db) {
            int result = -1;

            for (Enumeration serverIds = db.serverTable.keys(); serverIds.hasMoreElements();) {
                Integer nextServerId = (Integer) serverIds.nextElement();
                DBServerDef dbServerDef = (DBServerDef) db.serverTable.get(nextServerId);

                if (dbServerDef.applicationName.equals(applicationName)) {
                    result = nextServerId.intValue();
                    break;
                }
            }

            if (debug)
                System.out.println("RepositoryImpl: getServerID for " + applicationName + " is " + result);

            if (result == -1) {
                throw (new ServerNotRegistered());
            } else {
                return result;
            }
        }
    }

    public String[] getApplicationNames() {
        synchronized (db) {
            Vector v = new Vector();
            for (Enumeration serverIds = db.serverTable.keys(); serverIds.hasMoreElements();) {
                Integer nextServerId = (Integer) serverIds.nextElement();

                DBServerDef dbServerDef = (DBServerDef) db.serverTable.get(nextServerId);

                if (!dbServerDef.applicationName.equals(""))
                    v.addElement(dbServerDef.applicationName);
            }

            String[] apps = new String[v.size()];
            for (int i = 0; i < v.size(); i++) {
                apps[i] = (String) v.elementAt(i);
            }

            if (debug) {
                StringBuffer sb = new StringBuffer();
                for (int ctr = 0; ctr < apps.length; ctr++) {
                    sb.append(' ');
                    sb.append(apps[ctr]);
                }

                System.out.println("RepositoryImpl: getApplicationNames returns " + sb.toString());
            }

            return apps;
        }
    }

    /**
     * Typically the Repositoy is created within the ORBd VM but it can
     * be independently started as well.
     */
    public static void main(String args[]) {
        boolean debug = false;
        for (int ctr = 0; ctr < args.length; ctr++)
            if (args[ctr].equals("-debug"))
                debug = true;

        try {
            // See Bug 4396928 for more information about why we are
            // initializing the ORBClass to PIORB (now ORBImpl, but see the bug).
            Properties props = new Properties();
            props.put("org.omg.CORBA.ORBClass", "com.sun.corba.se.impl.orb.ORBImpl");
            ORB orb = (ORB) ORB.init(args, props);

            // create the repository object
            String db = System.getProperty(ORBConstants.DB_PROPERTY, ORBConstants.DEFAULT_DB_NAME);
            RepositoryImpl repository = new RepositoryImpl(orb, new File(db), debug);

            // wait for shutdown
            orb.run();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    transient private boolean debug = false;

    final static int illegalServerId = -1;

    transient private RepositoryDB db = null;

    transient ORB orb = null;

    transient ActivationSystemException wrapper;

    class RepositoryDB implements Serializable {
        File db;
        Hashtable serverTable;
        Integer serverIdCounter;

        RepositoryDB(File dbFile) {

            db = dbFile;

            // initialize the Server Id counter and hashtable.
            // the lower id range is reserved for system servers
            serverTable = new Hashtable(255);
            serverIdCounter = new Integer(256);
        }

        int incrementServerIdCounter() {
            int value = serverIdCounter.intValue();
            serverIdCounter = new Integer(++value);

            return value;
        }

        void flush() {
            try {
                db.delete();
                FileOutputStream fos = new FileOutputStream(db);
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                oos.writeObject(this);
                oos.flush();
                oos.close();
            } catch (Exception ex) {
                throw wrapper.cannotWriteRepositoryDb(ex);
            }
        }
    }

    class DBServerDef implements Serializable {
        public String toString() {
            return "DBServerDef(applicationName=" + applicationName + ", name=" + name + ", classPath=" + classPath + ", args=" + args + ", vmArgs=" + vmArgs + ", id=" + id + ", isInstalled=" + isInstalled + ")";
        }

        DBServerDef(ServerDef server, int server_id) {
            applicationName = server.applicationName;
            name = server.serverName;
            classPath = server.serverClassPath;
            args = server.serverArgs;
            vmArgs = server.serverVmArgs;
            id = server_id;
            isInstalled = false;
        }

        String applicationName;
        String name;
        String classPath;
        String args;
        String vmArgs;
        boolean isInstalled;
        int id;
    }
}
